#include <coreai/embedding/embedding.h>
#include <gio/gio.h>

#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
#include <vector>

void textEmbeddingSync() {
    TextEmbeddingSession *session = text_embedding_create_session();
    int initSession = text_embedding_init_session(session);
    if (initSession != 0) {
        std::cout << "init session failed" << std::endl;
    }

    // 同步接口
    EmbeddingResult *result = nullptr;
    bool sucess = text_embedding(session, "12345", &result);

    // 结果信息
    int error_code = embedding_result_get_error_code(result);
    std::cout << "error_code: " << error_code << std::endl;
    const char *error_message = embedding_result_get_error_message(result);
    std::cout << "error_message :" << error_message << std::endl;

    float *vector_result = embedding_result_get_vector_data(result);
    int len = embedding_result_get_vector_length(result);

    std::cout << "vector_result :" << std::endl;
    for (int i = 0; i < len; i++) {
        std::cout << vector_result[i] << ",";
    }
    std::cout << std::endl;

    char *info = nullptr;
    text_embedding_get_model_info(session, &info);
    std::cout << "print model info:" << info << std::endl;

    std::cout << "释放结果资源" << std::endl;
    embedding_result_destroy(&result);
    embedding_model_info_destroy(info);
    text_embedding_destroy_session(&session);
}

void callback(EmbeddingResult *result, void *callback_user_data) {
    // 结果信息
    int error_code = embedding_result_get_error_code(result);
    std::cout << "error_code: " << error_code << std::endl;
    const char *error_message = embedding_result_get_error_message(result);
    std::cout << "error_message :" << error_message << std::endl;

    float *vector_result = embedding_result_get_vector_data(result);
    int len = embedding_result_get_vector_length(result);

    std::cout << "vector_result :" << std::endl;
    for (int i = 0; i < len; i++) {
        std::cout << vector_result[i] << " ";
    }
    std::cout << std::endl;

    if (callback_user_data != nullptr) {
        int *a = static_cast<int *>(callback_user_data);
        std::cout << "user_data :" << *a << std::endl;
    }
}

void textEmbeddingAsync() {
    TextEmbeddingSession *session = text_embedding_create_session();
    if (text_embedding_init_session(session) != 0) {
        std::cout << "init session failed" << std::endl;
    }
    text_embedding_async(session, "热爱学习", callback, nullptr);
    auto *loop_ = g_main_loop_new(nullptr, false);
    std::thread ctrlThread([&session, loop_] {
        while (std::getchar() != '\n') {
        }
        g_main_loop_quit(loop_);
    });
    ctrlThread.detach();

    g_main_loop_run(loop_);
    text_embedding_destroy_session(&session);
}

std::vector<uint8_t> readFile(const std::string &filePath) {
    std::ifstream file(filePath, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file");
    }

    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);

    std::vector<uint8_t> buffer(size);
    if (file.read(reinterpret_cast<char *>(buffer.data()), size)) {
        return buffer;
    } else {
        throw std::runtime_error("Failed to read file");
    }
}
const std::string base64_chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";

std::string base64Encode(const std::vector<uint8_t> &buffer) {
    std::string encodedData;
    int i = 0;
    uint8_t char_array_3[3];
    uint8_t char_array_4[4];

    while (i < buffer.size()) {
        char_array_3[0] = buffer[i++];
        char_array_3[1] = (i < buffer.size()) ? buffer[i++] : 0;
        char_array_3[2] = (i < buffer.size()) ? buffer[i++] : 0;

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] =
            ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] =
            ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = (char_array_3[2] & 0x3f);

        for (int j = 0; (j < 4); ++j) {
            encodedData += base64_chars[char_array_4[j]];
        }
    }

    while ((encodedData.size() % 4) != 0) {
        encodedData += '=';
    }

    return encodedData;
}

void imageEmbeddingSync() {
    ImageEmbeddingSession *session = image_embedding_create_session();

    if (image_embedding_init_session(session) != 0) {
        std::cout << "init session failed" << std::endl;
    }
    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "embedding_image.png";

    std::string imagePath = path.string();
    std::vector<uint8_t> imageData = readFile(imagePath);
    std::string base64Data = base64Encode(imageData);
    unsigned int length = base64Data.length();
    const char *charPtr = base64Data.c_str();
    const unsigned char *ucharPtr =
        reinterpret_cast<const unsigned char *>(charPtr);

#if 0  // 同步向量化文本
    EmbeddingResult *result = nullptr;
    text_embedding_by_image_model(session, "do you love working?", &result);
#elif 1
    // 同步向量化图片
    EmbeddingResult *result = nullptr;
    image_embedding_by_image_file(session, imagePath.c_str(),
                                  &result);  // 换成自己路径的图片
#else
    // 同步向量化base64图片
    EmbeddingResult *result = nullptr;
    image_embedding_by_base64_image_data(session, ucharPtr, length, &result);
#endif
    // 结果信息
    int error_code = embedding_result_get_error_code(result);
    std::cout << "error_code: " << error_code << std::endl;
    const char *error_message = embedding_result_get_error_message(result);
    std::cout << "error_message :" << error_message << std::endl;

    float *vector_result = embedding_result_get_vector_data(result);
    int len = embedding_result_get_vector_length(result);

    std::cout << "vector_result :" << std::endl;
    for (int i = 0; i < len; i++) {
        std::cout << vector_result[i] << " ";
    }
    std::cout << std::endl;

    char *info = nullptr;
    image_embedding_get_model_info(session, &info);
    std::cout << "print model info:" << info << std::endl;

    embedding_result_destroy(&result);
    embedding_model_info_destroy(info);
    image_embedding_destroy_session(&session);
}

// async
void callbackAsync(EmbeddingResult *result, void *callback_user_data) {
    // 结果信息
    int error_code = embedding_result_get_error_code(result);
    std::cout << "error_code: " << error_code << std::endl;
    const char *error_message = embedding_result_get_error_message(result);
    std::cout << "error_message :" << error_message << std::endl;

    float *vector_result = embedding_result_get_vector_data(result);
    int len = embedding_result_get_vector_length(result);

    std::cout << "vector_result :" << std::endl;
    for (int i = 0; i < len; i++) {
        std::cout << vector_result[i] << " ";
    }
    std::cout << std::endl;

    if (callback_user_data != nullptr) {
        int *a = static_cast<int *>(callback_user_data);
        std::cout << "user_data :" << *a << std::endl;
    }
}

void imageEmbeddingAsync() {
    ImageEmbeddingSession *session = image_embedding_create_session();

    if (image_embedding_init_session(session) != 0) {
        std::cout << "init session failed" << std::endl;
    }

    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "embedding_image.png";

    std::string imagePath = path.string();
    std::vector<uint8_t> imageData = readFile(imagePath);
    std::string base64Data = base64Encode(imageData);
    unsigned int length = base64Data.length();
    const char *charPtr = base64Data.c_str();
    const unsigned char *ucharPtr =
        reinterpret_cast<const unsigned char *>(charPtr);

#if 1
    text_embedding_by_image_model_async(session, "do you love working?",
                                        callbackAsync, nullptr);
#elif 0
    image_embedding_from_by_file_async(session, imagePath.c_str(), callback,
                                       nullptr);
#else
    image_embedding_by_base64_image_data_async(session, ucharPtr, length,
                                               callback, nullptr);
#endif

    auto *loop_ = g_main_loop_new(nullptr, false);
    std::thread ctrlThread([&session, loop_] {
        while (std::getchar() != '\n') {
        }
        g_main_loop_quit(loop_);
    });
    ctrlThread.detach();

    g_main_loop_run(loop_);
    image_embedding_destroy_session(&session);
}
int main(int argc, char *argv[]) {
    //    textEmbeddingSync();
    //    textEmbeddingAsync();
    imageEmbeddingSync();
    //    imageEmbeddingAsync();
    return 0;
}
